package com.chungkui.bond.microservice.resource.config;


import com.chungkui.bond.authmanager.bean.Permission;
import com.chungkui.bond.common.core.bean.SecurityUser;
import com.chungkui.bond.microservice.resource.cache.PermissionCache;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;
import java.util.Set;

public class BondAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> {
    private final static Logger LOG = LoggerFactory.getLogger(BondAccessDecisionVoter.class);

    @Autowired
    PermissionCache PermissionCache;

    @Override
    public int vote(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
        assert authentication != null;
        assert object != null;
        if (authentication.getPrincipal() instanceof String) {
            return voteClient(authentication, object, attributes);
        } else if (authentication.getPrincipal() instanceof SecurityUser) {
            return voteWeb(authentication, object, attributes);
        }
        return ACCESS_DENIED;
    }

    private int voteWeb(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
        // 拿到当前请求uri
        String requestUrl = object.getRequestUrl();
        String method = object.getRequest().getMethod();
        /*内部调用走内部逻辑*/
        LOG.debug("进入自定义鉴权投票器，URI : {} {}", method, requestUrl);
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<Permission> list = PermissionCache.getAll();
        Permission permission = null;
        for (Permission p : list) {
            if (StringUtils.isNotEmpty(p.getUrl()) && antPathMatcher.match(p.getUrl(), requestUrl)) {
                permission = p;
            }
        }
        /**
         * 不需要控制权限
         */
        if (permission == null) {
            return ACCESS_ABSTAIN;
        }
        Set<String> roles = permission.getRoles();
        if(CollectionUtils.isEmpty(roles)){
            return ACCESS_ABSTAIN;
        }
        //用户拥有的角色
        Collection<? extends GrantedAuthority> authorities = ((SecurityUser) authentication.getPrincipal()).getAuthorities();

        for (int i = 0; i < roles.size(); i++) {
            for (GrantedAuthority a : authorities) {
                if (roles.contains(a.getAuthority())) {
                    return ACCESS_GRANTED;
                }
            }
        }
        return ACCESS_DENIED;
    }

    private int voteClient(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
        // 拿到当前请求uri
        String requestUrl = object.getRequestUrl();
        String method = object.getRequest().getMethod();
        /*内部调用走内部逻辑*/
        LOG.debug("进入自定义鉴权投票器，URI : {} {}", method, requestUrl);
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<Permission> list = PermissionCache.getAll();
        Permission permission = null;
        for (Permission p : list) {
            if (StringUtils.isNotEmpty(p.getUrl()) && antPathMatcher.match(p.getUrl(), requestUrl)) {
                permission = p;
            }
        }
        /**
         * 不需要控制权限
         */
        if (permission == null) {
            return ACCESS_ABSTAIN;
        }
        Set<String> clients = permission.getAllowClients();
        //客户端id
        String clientId = ((String) authentication.getPrincipal());
        if (CollectionUtils.isNotEmpty(clients)) {
            for (String c : clients) {
                if (StringUtils.equals(c, clientId)) {
                    return ACCESS_GRANTED;
                }
            }
            return ACCESS_DENIED;
        } else {
            return ACCESS_GRANTED;
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    public PermissionCache getPermissionCache() {
        return PermissionCache;
    }

    public void setPermissionCache(PermissionCache permissionCache) {
        PermissionCache = permissionCache;
    }
}

